/**
* @FILENAME fmd.cpp
* @BRIEF    The file is an flash media driver PDD driver.
* @BRIEF    It will call the nand driver and cover up the chip feature.
* @BRIEF    and this code is in kernel mode
* @BRIEF    
* Copyright (C) 2004 Anyka (Guangzhou) Software Technology Co., LTD
* @AUTHOR   Linjie
* @DATE     2008-03-12
* @VERSION  0.1
* @
*/
#include <windows.h>
#include <types.h>
#include <fmd.h>
#include <ANYKA.h>
#include <oal.h>
#include <ethdbg.h>
#include <Winbase.h>
#include <Pkfuncs.h>
#include <nkintr.h>
#include "nandflash.h"
#include "nand.h"
#include <fmdak.h>
//------------------------------------------------------------------------------
#ifdef FAL_POWEROFF_TEST
extern "C" { 
extern T_FMD_TEST tFmdTestSet;
extern T_FMD_TEST tFmdTestCur;
}
#endif
//------------------------------------------------------------------------------
extern "C" PL2F_FUNC DDK_L2FIFO_Interface(void);
//------------------------------------------------------------------------------
T_PCHIP_INFO pChipInfo;
//------------------------------------------------------------------------------
T_NAND_INFO Nand_Info;
T_PNAND_INFO pNand_Info = NULL;
static T_NAND_CONTROL NandCtl = {NULL, NULL, NULL, NULL, NULL, NULL};
static PL2F_FUNC p_L2func = NULL;
static BYTE* g_pSectBuf=NULL;
//------------------------------------------------------------------------------
#define FMD_USE_DMA
//------------------------------------------------------------------------------
#ifdef FMD_USE_DMA
static unsigned char *pVirL2DMAWrBuffer;
static DWORD PhyL2DMAWrBuffer;
static unsigned char *pVirL2DMARdBuffer;
static DWORD PhyL2DMARdBuffer;
static ULONG WrTransAddr;
static ULONG RdTransAddr;
#endif  /*FMD_USE_DMA*/
//------------------------------------------------------------------------------
static BOOL FifoInit(void)
{
    //ULONG InBuf[2];
    //ULONG OutBuf;
#ifdef FMD_USE_DMA
    pVirL2DMAWrBuffer = (unsigned char *)AllocPhysMem(64*1024, PAGE_READWRITE, 0, 0, &PhyL2DMAWrBuffer);
    if(pVirL2DMAWrBuffer == NULL)
    {
        DEBUGMSG(1, (TEXT("AllocPhysMem 1 Error %d!!!\r\n"), GetLastError()));
        return FALSE;
    }
    pVirL2DMARdBuffer = (unsigned char *)AllocPhysMem(64*1024, PAGE_READWRITE, 0, 0,&PhyL2DMARdBuffer);
    if(pVirL2DMARdBuffer == NULL)
    {
        DEBUGMSG(1, (TEXT("AllocPhysMem 2 Error %d!!!\r\n"), GetLastError()));
        return FALSE;
    }
    DEBUGMSG(1, (TEXT("pVirL2DMAWrBuffer = 0x%x\r\n"), pVirL2DMAWrBuffer));
    DEBUGMSG(1, (TEXT("pVirL2DMARdBuffer = 0x%x\r\n"), pVirL2DMARdBuffer));
    if(!LockPages((LPVOID)pVirL2DMAWrBuffer, 4096, &WrTransAddr, LOCKFLAG_READ))
    {   
    	DEBUGMSG(1, (TEXT("LockPages Error!!!\r\n")));
    	return FALSE;
    }
    
    if(!LockPages((LPVOID)pVirL2DMARdBuffer, 4096, &RdTransAddr, LOCKFLAG_WRITE))
    {   
    	DEBUGMSG(1, (TEXT("LockPages Error!!!\r\n")));
    	return FALSE;
    }
    DEBUGMSG(1, (TEXT("WrTransAddr = 0x%x\r\n"), WrTransAddr));
    DEBUGMSG(1, (TEXT("RdTransAddr = 0x%x\r\n"), RdTransAddr));
#endif  /*FMD_USE_DMA*/
    //InBuf[0] = NFC_RW;
    //InBuf[1] = 1;
	//KernelIoControl(IOCTL_HAL_L2_ALLOCBUFS, InBuf, sizeof(InBuf), &OutBuf, sizeof(OutBuf), NULL);
	p_L2func = DDK_L2FIFO_Interface();
	return p_L2func->AllocBufs(NFC_RW, 1);
}

static ULONG Fifo2Memory(BYTE *pRamAddr, UINT32 size)
{
    //FIFO -> RdTransAddr -> pVirL2DMARdBuffer -> pRamAddr
    L2F_HEAD L2fifo;
    //ULONG OutBuf;
    
    L2fifo.Count = size;
    L2fifo.L2BufType = NFC_RW;
    L2fifo.BufOffset = 0;
#ifdef FMD_USE_DMA
    if(size < 512)
    {
        L2fifo.TransType = L2_CPU_MODE;
        L2fifo.RamAdr = (ULONG)pRamAddr;
    }
    else
    {
        L2fifo.TransType = L2_DMA_MODE;
        L2fifo.RamAdr = (ULONG)RdTransAddr;
    }
#else  /*FMD_USE_DMA*/
    L2fifo.TransType = L2_CPU_MODE;
    L2fifo.RamAdr = (ULONG)pRamAddr;
#endif  /*FMD_USE_DMA*/
    //KernelIoControl(IOCTL_HAL_L2_FIFO2RAM, (LPVOID)&L2fifo, sizeof(L2F_HEAD), &OutBuf, sizeof(OutBuf), NULL);
    p_L2func->FIFO2Ram(&L2fifo);
#ifdef FMD_USE_DMA
    if(size >= 512)
    {
        memcpy(pRamAddr, pVirL2DMARdBuffer, size);
    }
#endif  /*FMD_USE_DMA*/
    return size;
}

static ULONG Memory2Fifo(BYTE *pRamAddr, UINT32 size)
{
    //pRamAddr -> pVirL2DMAWrBuffer -> WrTransAddr -> FIFO
    L2F_HEAD L2fifo;
    //ULONG OutBuf;
    L2fifo.Count = size;
    L2fifo.L2BufType = NFC_RW;
    L2fifo.BufOffset = 0;
#ifdef FMD_USE_DMA
    if(size < 512)
    {
        L2fifo.TransType = L2_CPU_MODE;
        L2fifo.RamAdr = (ULONG)pRamAddr;
    }
    else
    {
        memcpy(pVirL2DMAWrBuffer, pRamAddr, size);
        L2fifo.TransType = L2_DMA_MODE;
        L2fifo.RamAdr = (ULONG)WrTransAddr;
    }
#else  /*FMD_USE_DMA*/
    L2fifo.TransType = L2_CPU_MODE;
    L2fifo.RamAdr = (ULONG)pRamAddr;
#endif  /*FMD_USE_DMA*/
    //KernelIoControl(IOCTL_HAL_L2_RAM2FIFO, (LPVOID)&L2fifo, sizeof(L2F_HEAD), &OutBuf, sizeof(OutBuf), NULL);
    return p_L2func->Ram2FIFO(&L2fifo);
}

static void FifoReset(void)
{
    L2F_HEAD L2fifo;
	//ULONG OutBuf;
    L2fifo.L2BufType = NFC_RW;
    //KernelIoControl(IOCTL_HAL_L2_CLEARFLAG, (LPVOID)&L2fifo, sizeof(L2F_HEAD), &OutBuf, sizeof(OutBuf), NULL);
    p_L2func->ClearFlag(&L2fifo);
}

BOOL Wait(UINT32 TimeOutNs)
{
    if(TimeOutNs>1000)
    {
        Sleep(1);
    }
    return TRUE;
}

//------------------------------------------------------------------------------
/**
* @BRIEF    convert FMD Block id to Nand address
* @AUTHOR   linjie
* @DATE     2007-12-14
* @PARAM    SectorAddr: in, fmd sector address
            chip: out, nand flash chip id
            rowAddr: out, nand flash row address
            columnAddr: out, nand flash column address
* @RETURN   TRUE: convert sucess
* @RETVAL
* @NOTE
*/
inline BOOL Addr_FMD2NAND(SECTOR_ADDR SectorAddr, ULONG *chip, ULONG *rowAddr, ULONG *columnAddr)
{
    *chip = SectorAddr / Nand_Info.FalSectorsPerChip;
    *rowAddr = (SectorAddr % Nand_Info.FalSectorsPerChip) << Nand_Info.CvtRowAddrShift;
    *columnAddr = 0;
    return TRUE;
}
//------------------------------------------------------------------------------
/**
* @BRIEF    Initializes the Smart Media NAND flash controller.
* @AUTHOR   linjie
* @DATE     2008-1-6
* @PARAM    
* @RETURN
* @RETVAL
* @NOTE
*/
PVOID FMD_Init(LPCTSTR lpActiveReg, PPCI_REG_INFO pRegIn, PPCI_REG_INFO pRegOut)        /* */
{
    BOOL bLastMode;
    
    if(pNand_Info != NULL)
    {
        return((PVOID)pNand_Info);
    }
    else
    {
        pNand_Info = &Nand_Info;
    }
    
    NandCtl.pfnFifoInit     = FifoInit;
	NandCtl.pfnFifoReset    = FifoReset;
	NandCtl.pfnFifo2Memory  = Fifo2Memory;
	NandCtl.pfnMemory2Fifo  = Memory2Fifo;
	NandCtl.pfnWaitCommandDone  = Wait;
    bLastMode = SetKMode(TRUE);
    pChipInfo = nand_init(&NandCtl);
    SetKMode (bLastMode);
    if(pChipInfo == NULL)
    {
        pNand_Info = NULL;
        goto FailedOut;
    }
    
    if(NFLASH_LARGE2K_PAGE == pChipInfo->ChipType)
    {
        Nand_Info.CvtRowAddrShift = 0;              //depend on FAL_BYTE_PER_SECTOR
        Nand_Info.FalBytesPerSector = FAL_BYTE_PER_SECTOR;
    }
    else if(NFLASH_LARGE4K_PAGE == pChipInfo->ChipType)
    {
        Nand_Info.CvtRowAddrShift = 0;              //depend on FAL_BYTE_PER_SECTOR
        Nand_Info.FalBytesPerSector = 4096;
    }
    else if(NFLASH_SMALL_PAGE == pChipInfo->ChipType)
    {
        Nand_Info.CvtRowAddrShift = 2;              //depend on FAL_BYTE_PER_SECTOR
        Nand_Info.FalBytesPerSector = FAL_BYTE_PER_SECTOR;
    }
    else
    {
        //need to treat with????
    }
    //this is use for nand
    Nand_Info.Planes    = pChipInfo->PlanePerChip * pChipInfo->ChipCnt;
    Nand_Info.Blocks    = pChipInfo->BlockPerPlane * Nand_Info.Planes;
    Nand_Info.Pages     = pChipInfo->PagePerBlock * Nand_Info.Blocks;
    Nand_Info.Bytes     = pChipInfo->BytesPerPage * Nand_Info.Pages;
    Nand_Info.BytesPerBlock = pChipInfo->BytesPerPage * pChipInfo->PagePerBlock;
    //this is use for FMD
    Nand_Info.FalSectorsPerBlock = (WORD)(Nand_Info.BytesPerBlock / Nand_Info.FalBytesPerSector);
    Nand_Info.FalSectorsPerChip = Nand_Info.FalSectorsPerBlock * pChipInfo->BlockPerPlane * pChipInfo->PlanePerChip;

	g_pSectBuf=(BYTE*)LocalAlloc(LMEM_FIXED, Nand_Info.FalBytesPerSector);
	if(NULL==g_pSectBuf)
	{
		return NULL;
	}

#ifdef USE_INT_EVENT
    ghStartEvent = CreateEvent(0, FALSE, FALSE, _T("NStart"));
    if(ghStartEvent == NULL)
    {
        DEBUGMSG(1, (TEXT("xxxx Create start Event Error!\r\n")));
    }
    if(!InterruptInitialize(SYSINTR_HOST_NAND, ghStartEvent, NULL, 0))
    {
        DEBUGMSG(1, (TEXT("xxxx InterruptInitialize Error!\r\n")));
        CloseHandle(ghStartEvent);
    }
#endif

#ifdef FAL_POWEROFF_TEST
    PHYSICAL_ADDRESS GpioPhyAddr;
    PUCHAR pGpioBase;
    
    tFmdTestSet.Mode        = 0;                 
    tFmdTestSet.WriteTimes  = 0;
    tFmdTestSet.ReadTimes   = 0;
    tFmdTestSet.WrWaitTimes = 0;
    tFmdTestSet.RdWaitTimes = 0;
    tFmdTestSet.PowerOff    = 0;
    
    tFmdTestCur.Mode        = 0;
    tFmdTestCur.WriteTimes  = 0;
    tFmdTestCur.ReadTimes   = 0;
    tFmdTestCur.WrWaitTimes = 0;
    tFmdTestCur.RdWaitTimes = 0;
    tFmdTestCur.PowerOff   = 0;
    GpioPhyAddr.LowPart = AK_BASE_REG_PA_GPIO;
    GpioPhyAddr.HighPart = 0;
    pGpioBase = (PUCHAR)MmMapIoSpace(GpioPhyAddr, 0x200, FALSE);
    if (NULL == pGpioBase)
    {
        DEBUGCHK(1);
    }
    power_off_init(pGpioBase);
#endif
FailedOut:
    return((PVOID)pNand_Info);
}
//------------------------------------------------------------------------------/*ok*/
/**
* @BRIEF    ads the specified sector(s) from NAND flash
* @AUTHOR   linjie
* @DATE     2008-1-6
* @PARAM    
* @RETURN
* @RETVAL
* @NOTE
*/
BOOL FMD_ReadSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)
{
    ULONG SectorAddr = (ULONG)startSectorAddr;
    ULONG chip = 0, rowAddr = 0, columnAddr = 0;
    BYTE spare[8];
    ULONG status;
    BOOL bLastMode;
    bLastMode = SetKMode(TRUE);

    ASSERT(1==dwNumSectors);
	if(NULL == pSectorBuff)
	{
		pSectorBuff=g_pSectBuf;
	}

    while (dwNumSectors--)
    {
        Addr_FMD2NAND(SectorAddr, &chip, &rowAddr, &columnAddr);

        if(NFLASH_SMALL_PAGE == pChipInfo->ChipType)
        {
            status = nand_readsector_512(chip, rowAddr, pSectorBuff, spare);
            if(status >= RDSECTOR_ERROR)
            {
                goto FailedOut;
            }
            else if(status == RDSECTOR_EMPTY)
            {
                memset(pSectorBuff, 0xff, 2048); 
                memset((char *)spare, 0xff, 8);
            }
            else
            {
                rowAddr ++;
                if(nand_readsector_512(chip, rowAddr, pSectorBuff + NFC_FIFO_SIZE * 1, spare + 4) >= RDSECTOR_EMPTY)
                {
                    goto FailedOut;
                }
                rowAddr ++;
                if(nand_readsector_512(chip, rowAddr, pSectorBuff + NFC_FIFO_SIZE * 2, spare) >= RDSECTOR_EMPTY)
                {
                    goto FailedOut;
                }
                rowAddr ++;
                if(nand_readsector_512(chip, rowAddr, pSectorBuff + NFC_FIFO_SIZE * 3, spare + 4) >= RDSECTOR_EMPTY)
                {
                    goto FailedOut;
                }
            }
        }
        else if(NFLASH_LARGE2K_PAGE == pChipInfo->ChipType)
        {
            if(nand_readsector_2k(chip, rowAddr, pSectorBuff, (BYTE *)spare) >= RDSECTOR_ERROR)
            {
                goto FailedOut;
            }
        }
        else
        {
            if(nand_readsector_4k(chip, rowAddr, pSectorBuff, spare) >= RDSECTOR_ERROR)
            {
                goto FailedOut;
            }
        }
        pSectorBuff += Nand_Info.FalBytesPerSector;
        if (pSectorInfoBuff)
        {               
            memcpy((LPBYTE)pSectorInfoBuff, spare, sizeof(SectorInfo));       
            pSectorInfoBuff++;
        }

        SectorAddr++;
    }
    SetKMode (bLastMode);
    return(TRUE);
FailedOut:
    SetKMode (bLastMode);
    return FALSE;
}

int  FMD_ReadSectorAk(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfoAk pSectorInfoBuff, DWORD dwNumSectors)
{
    ULONG SectorAddr = (ULONG)startSectorAddr;
    ULONG chip = 0, rowAddr = 0, columnAddr = 0;
    BYTE spare[sizeof(SectorInfoAk)];
    ULONG status;
    BOOL bLastMode;
    bLastMode = SetKMode(TRUE);
    int ret = FMDAK_READ_RET_OK;
    
    ASSERT(1==dwNumSectors);
	if(NULL == pSectorBuff)
	{
		pSectorBuff=g_pSectBuf;
	}

    while (dwNumSectors--)
    {
        Addr_FMD2NAND(SectorAddr, &chip, &rowAddr, &columnAddr);

        if(NFLASH_SMALL_PAGE == pChipInfo->ChipType)
        {
            status = nand_readsector_512(chip, rowAddr, pSectorBuff, spare);
            if(status >= RDSECTOR_ERROR)
            {
                ret = FMDAK_READ_RET_ERR_BLOCK_BAD;
                goto EXIT;
            }
            else if(status == RDSECTOR_EMPTY)
            {
                memset(pSectorBuff, 0xff, 2048); 
                memset((char *)spare, 0xff, sizeof(SectorInfoAk));
            }
            else
            {
                rowAddr ++;
                if(nand_readsector_512(chip, rowAddr, pSectorBuff + NFC_FIFO_SIZE * 1, spare + 4) >= RDSECTOR_EMPTY)
                {
                    ret = FMDAK_READ_RET_ERR_BLOCK_BAD;
                    goto EXIT;
                }
                rowAddr ++;
                if(nand_readsector_512(chip, rowAddr, pSectorBuff + NFC_FIFO_SIZE * 2, spare + 8) >= RDSECTOR_EMPTY)
                {
                    ret = FMDAK_READ_RET_ERR_BLOCK_BAD;
                    goto EXIT;
                }
                rowAddr ++;
                if(nand_readsector_512(chip, rowAddr, pSectorBuff + NFC_FIFO_SIZE * 3, spare + 12) >= RDSECTOR_EMPTY)
                {
                    ret = FMDAK_READ_RET_ERR_BLOCK_BAD;
                    goto EXIT;
                }
            }
        }
        else if(NFLASH_LARGE2K_PAGE == pChipInfo->ChipType)
        {
            if(nand_readsector_2k_AK(chip, rowAddr, pSectorBuff, (BYTE *)spare) >= RDSECTOR_ERROR)
            {
                ret = FMDAK_READ_RET_ERR_BLOCK_BAD;
                goto EXIT;
            }
        }
        else // NFLASH_LARGE4K_PAGE == pChipInfo->ChipType
        {
            if(nand_readsector_4k_AK(chip, rowAddr, pSectorBuff, spare) >= RDSECTOR_ERROR)
            {
                ret = FMDAK_READ_RET_ERR_BLOCK_BAD;
                goto EXIT;
            }
        }
        pSectorBuff += Nand_Info.FalBytesPerSector;
        if (pSectorInfoBuff)
        {               
            memcpy((LPBYTE)pSectorInfoBuff, spare, sizeof(SectorInfoAk));       
            pSectorInfoBuff++;
        }

        SectorAddr++;
    }
EXIT:
    SetKMode (bLastMode);
    return ret;
}

//------------------------------------------------------------------------------
/**
* @BRIEF    Erases the specified flash block.
* @AUTHOR   linjie
* @DATE     2008-1-6
* @PARAM    
* @RETURN
* @RETVAL
* @NOTE
*/
BOOL FMD_EraseBlock(BLOCK_ID blockID)
{
    BOOL bLastMode;
    BOOL s;
    bLastMode = SetKMode(TRUE);
    s = nand_erase_block(blockID);
    SetKMode (bLastMode);
    return s;
}


//------------------------------------------------------------------------------
/**
* @BRIEF    Writes the specified data to the specified NAND flash sector/page.
* @AUTHOR   linjie
* @DATE     2008-1-6
* @PARAM    
* @RETURN
* @RETVAL
* @NOTE
*/
BOOL FMD_WriteSector(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfo pSectorInfoBuff, DWORD dwNumSectors)
{
    BOOL bLastMode;
    
    bLastMode = SetKMode(TRUE);
    BYTE TempInfo[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
    ULONG SectorAddr = (ULONG)startSectorAddr;
    ULONG rowAddr = 0, chip = 0, columnAddr = 0;
    PULONG pSpare;

    //Edit by duke;2007-09-10
    DWORD dwTry = 0;
    DWORD dwTempVal = 0;
    DWORD dwNandVal = 0;
    //End Edit by duke;2007-09-10
    
    //DEBUGMSG(1, (TEXT("Write = %x %x %x\r\n"), startSectorAddr, (ULONG)pSectorBuff, (ULONG)((PULONG)pSectorInfoBuff)));
    while (dwNumSectors--)
    {
        Addr_FMD2NAND(SectorAddr, &chip, &rowAddr, &columnAddr);
        if(NULL == pSectorBuff)
        {
            if(NULL == pSectorInfoBuff)
            {
                DEBUGMSG(1, (TEXT("FMD_WriteSector spare data buf is null!!!\r\n")));
                goto FailedOut;
            }       

            if(NFLASH_SMALL_PAGE == pChipInfo->ChipType)
            {
                if(!nand_write_spare_512b(chip, rowAddr, LPBYTE(pSectorInfoBuff)))
                   goto FailedOut;
                rowAddr++;
                if(!nand_write_spare_512b(chip, rowAddr, LPBYTE(pSectorInfoBuff) + 4))
                    goto FailedOut;
            }
            else if(NFLASH_LARGE2K_PAGE == pChipInfo->ChipType)
            {
                if(!nand_write_spare_2k(chip, rowAddr, LPBYTE(pSectorInfoBuff)))
                    goto FailedOut;
            }
            else //NFLASH_LARGE4K_PAGE
            {
                if(!nand_write_spare_4k(chip, rowAddr, LPBYTE(pSectorInfoBuff)))
                    goto FailedOut;
            }
            
            pSectorInfoBuff++;   
        }
        else
        {
            if(NULL == pSectorInfoBuff)
            {
                pSpare = (PULONG)TempInfo;
            }
            else
            {
                pSpare = (PULONG)pSectorInfoBuff;
            }
            if(NFLASH_SMALL_PAGE == pChipInfo->ChipType)
            {
                if(nand_writesector_512(chip, rowAddr, pSectorBuff, (BYTE *)pSpare) != 0)
                {
                    goto FailedOut;
                }
                rowAddr ++;
                if(nand_writesector_512(chip, rowAddr, pSectorBuff + NFC_FIFO_SIZE * 1, (BYTE *)(pSpare + 1)) != 0)
                {
                    goto FailedOut;
                }
                rowAddr ++;
                if(nand_writesector_512(chip, rowAddr, pSectorBuff + NFC_FIFO_SIZE * 2, (BYTE *)pSpare) != 0)
                {
                    goto FailedOut;
                }
                rowAddr ++;
                if(nand_writesector_512(chip, rowAddr, pSectorBuff + NFC_FIFO_SIZE * 3, (BYTE *)(pSpare + 1)) != 0)
                {
                    goto FailedOut;
                }
            }
            else if(NFLASH_LARGE2K_PAGE == pChipInfo->ChipType)
            {
                if(nand_writesector_2k(chip, rowAddr, pSectorBuff, (BYTE *)pSpare) != 0)
                {
                    goto FailedOut;
                }
            }
            else    //NFLASH_LARGE4K_PAGE
            {
                if(nand_writesector_4k(chip, rowAddr, pSectorBuff, (BYTE *)pSpare) != 0)
                {
                    goto FailedOut;
                }
            }
            if(pSectorInfoBuff != NULL)
            {
                pSectorInfoBuff++;
            }
            pSectorBuff += Nand_Info.FalBytesPerSector;
        }
        SectorAddr++;
    }
    SetKMode (bLastMode);
    return(TRUE);
FailedOut:
    SetKMode (bLastMode);
    return FALSE;
}

BOOL  FMD_WriteSectorAk(SECTOR_ADDR startSectorAddr, LPBYTE pSectorBuff, PSectorInfoAk pSectorInfoBuff, DWORD dwNumSectors)
{
    BOOL bLastMode;
    
    bLastMode = SetKMode(TRUE);
    BYTE TempInfo[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
    ULONG SectorAddr = (ULONG)startSectorAddr;
    ULONG rowAddr = 0, chip = 0, columnAddr = 0;
    PBYTE pSpare;

    //Edit by duke;2007-09-10
    DWORD dwTry = 0;
    DWORD dwTempVal = 0;
    DWORD dwNandVal = 0;
    //End Edit by duke;2007-09-10
    
    //DEBUGMSG(1, (TEXT("Write = %x %x %x\r\n"), startSectorAddr, (ULONG)pSectorBuff, (ULONG)((PULONG)pSectorInfoBuff)));
    while (dwNumSectors--)
    {
        Addr_FMD2NAND(SectorAddr, &chip, &rowAddr, &columnAddr);
        if(NULL == pSectorBuff)
        {
            if(NULL == pSectorInfoBuff)
            {
                DEBUGMSG(1, (TEXT("FMD_WriteSector spare data buf is null!!!\r\n")));
                goto FailedOut;
            }       

            if(NFLASH_SMALL_PAGE == pChipInfo->ChipType)
            {
                if(!nand_write_spare_512b(chip, rowAddr, LPBYTE(pSectorInfoBuff)))
                   goto FailedOut;
                rowAddr++;
                if(!nand_write_spare_512b(chip, rowAddr, LPBYTE(pSectorInfoBuff) + 4))
                    goto FailedOut;
                rowAddr++;
                if(!nand_write_spare_512b(chip, rowAddr, LPBYTE(pSectorInfoBuff) + 8))
                    goto FailedOut;
                rowAddr++;
                if(!nand_write_spare_512b(chip, rowAddr, LPBYTE(pSectorInfoBuff) + 12))
                    goto FailedOut;
            }
            else if(NFLASH_LARGE2K_PAGE == pChipInfo->ChipType)
            {
                if(!nand_write_spare_2k_AK(chip, rowAddr, LPBYTE(pSectorInfoBuff)))
                    goto FailedOut;
            }
            else    //NFLASH_LARGE4K_PAGE
            {
                if(!nand_write_spare_4k_AK(chip, rowAddr, LPBYTE(pSectorInfoBuff)))
                    goto FailedOut;
            }
                
            pSectorInfoBuff++;   
        }
        else
        {
            if(NULL == pSectorInfoBuff)
            {
                pSpare = TempInfo;
            }
            else
            {
                pSpare = (PBYTE)pSectorInfoBuff;
            }
            if(NFLASH_SMALL_PAGE == pChipInfo->ChipType)
            {
                if(nand_writesector_512(chip, rowAddr, pSectorBuff, pSpare) != 0)
                {
                    goto FailedOut;
                }
                rowAddr ++;
                if(nand_writesector_512(chip, rowAddr, pSectorBuff + NFC_FIFO_SIZE * 1, (pSpare + 4)) != 0)
                {
                    goto FailedOut;
                }
                rowAddr ++;
                if(nand_writesector_512(chip, rowAddr, pSectorBuff + NFC_FIFO_SIZE * 2, pSpare + 8) != 0)
                {
                    goto FailedOut;
                }
                rowAddr ++;
                if(nand_writesector_512(chip, rowAddr, pSectorBuff + NFC_FIFO_SIZE * 3, (pSpare + 12)) != 0)
                {
                    goto FailedOut;
                }
            }
            else if(NFLASH_LARGE2K_PAGE == pChipInfo->ChipType)
            {
                if(nand_writesector_2k_AK(chip, rowAddr, pSectorBuff, (BYTE *)pSpare) != 0)
                {
                    goto FailedOut;
                }
            }
            else    //NFLASH_LARGE4K_PAGE
            {
                if(nand_writesector_4k_AK(chip, rowAddr, pSectorBuff, (BYTE *)pSpare) != 0)
                {
                    goto FailedOut;
                }
            }
            if(pSectorInfoBuff != NULL)
            {
                pSectorInfoBuff++;
            }
            pSectorBuff += Nand_Info.FalBytesPerSector;
        }
        SectorAddr++;
    }
    SetKMode (bLastMode);
    return(TRUE);
FailedOut:
    SetKMode (bLastMode);
    return FALSE;
}


//------------------------------------------------------------------------------
/**
* @BRIEF    power up
* @AUTHOR   zhaojiahuan
* @DATE     2007-7-9
* @PARAM    
* @RETURN
* @RETVAL
* @NOTE
*/
VOID FMD_PowerUp(VOID)
{
}
//------------------------------------------------------------------------------
/**
* @BRIEF    power down
* @AUTHOR   zhaojiahuan
* @DATE     2007-7-9
* @PARAM    
* @RETURN
* @RETVAL
* @NOTE
*/
VOID FMD_PowerDown(VOID)
{
}
//------------------------------------------------------------------------------
/**
* @BRIEF    io control
* @AUTHOR   zhaojiahuan
* @DATE     2007-7-9
* @PARAM    
* @RETURN
* @RETVAL
* @NOTE
*/
BOOL FMD_OEMIoControl(DWORD dwIoControlCode, PBYTE pInBuf, DWORD nInBufSize, PBYTE pOutBuf, DWORD nOutBufSize, PDWORD pBytesReturned)
{
    if(IOCTL_HAL_FMD_TEST_SETTING == dwIoControlCode)
    {
#ifdef FAL_POWEROFF_TEST
        PT_FMD_TEST ptFmdTest = (PT_FMD_TEST)pInBuf;
        if(ptFmdTest->PowerOff == 1)
        {
            power_off();
            DEBUGMSG(1, (TEXT("Power Off Direct\n")));
        }
        DEBUGMSG(1, (TEXT("FMD_OEMIoControl set mode %d!\r\n"), ptFmdTest->Mode));
        tFmdTestSet.Mode        = ptFmdTest->Mode;
        tFmdTestSet.WriteTimes  = ptFmdTest->WriteTimes;
        tFmdTestSet.ReadTimes   = ptFmdTest->ReadTimes;
        tFmdTestSet.WrWaitTimes = ptFmdTest->WrWaitTimes;
        tFmdTestSet.RdWaitTimes = ptFmdTest->RdWaitTimes;
        DEBUGMSG(1, (TEXT("WriteTimes %d!\r\n"), tFmdTestSet.WriteTimes));
        DEBUGMSG(1, (TEXT("ReadTimes %d!\r\n"), tFmdTestSet.ReadTimes));
        DEBUGMSG(1, (TEXT("WrWaitTimes %d!\r\n"), tFmdTestSet.WrWaitTimes));
        DEBUGMSG(1, (TEXT("RdWaitTimes %d!\r\n"), tFmdTestSet.RdWaitTimes));

        tFmdTestCur.Mode        = ptFmdTest->Mode;       
        tFmdTestCur.WriteTimes  = ptFmdTest->WriteTimes; 
        tFmdTestCur.ReadTimes   = ptFmdTest->ReadTimes;  
        tFmdTestCur.WrWaitTimes = ptFmdTest->WrWaitTimes;
        tFmdTestCur.RdWaitTimes = ptFmdTest->RdWaitTimes;
        return(TRUE);
#endif
    }
    return(FALSE);
}
//------------------------------------------------------------------------------
/**
* @BRIEF    deinit
* @AUTHOR   zhaojiahuan
* @DATE     2007-7-9
* @PARAM    
* @RETURN
* @RETVAL
* @NOTE
*/
BOOL FMD_Deinit(PVOID hFMD)
{
    pNand_Info = NULL;
	if(NULL!=g_pSectBuf)
	{
		LocalFree(g_pSectBuf);
		g_pSectBuf=NULL;
	}

#ifdef USE_INT_EVENT
    CloseHandle(ghStartEvent);
    InterruptDisable(SYSINTR_HOST_NAND);
#endif

    if(p_L2func)
    {
        p_L2func->FreeBufs(NFC_RW);
    }

    return(TRUE);
}
//------------------------------------------------------------------------------
/**
* @BRIEF    Provides information on the NAND flash.
* @AUTHOR   linjie
* @DATE     2008-1-6
* @PARAM    
* @RETURN
* @RETVAL
* @NOTE
*/
BOOL FMD_GetInfo(PFlashInfo pFlashInfo)
{
    if (!pFlashInfo)
    {
        return(FALSE);
    }
    pFlashInfo->flashType           = NAND;
    pFlashInfo->dwNumBlocks         = Nand_Info.Blocks;
    pFlashInfo->dwBytesPerBlock     = Nand_Info.BytesPerBlock;
    pFlashInfo->wSectorsPerBlock    = Nand_Info.FalSectorsPerBlock;
    pFlashInfo->wDataBytesPerSector = Nand_Info.FalBytesPerSector;
    return(TRUE);
}
//------------------------------------------------------------------------------
/**
* @BRIEF    get the bad block mark
* @AUTHOR   linjie
* @DATE     2008-1-6
* @PARAM    
* @RETURN
* @RETVAL
* @NOTE
*/
static BOOL IsBlockBad(BLOCK_ID blockID)
{
    BYTE Data[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
    ULONG rowAddr;
    ULONG columnAddr;
	ULONG chip;
    BOOL bLastMode;
    chip = blockID / pChipInfo->BlockPerChip;
    rowAddr = (blockID % pChipInfo->BlockPerChip) * pChipInfo->PagePerBlock;
    //if(NFLASH_SMALL_PAGE == pChipInfo->ChipType)
    //    rowAddr <<= 2;
    if(NFLASH_SMALL_PAGE == pChipInfo->ChipType)
    {
        rowAddr += 1;
        columnAddr = NFC_LOG_PAGE_SIZE;
    }
    else
    {
        columnAddr = NFC_LOG_PAGE_SIZE + NFC_LOG_SPARE_SIZE + NFC_LOG_PAGE_SIZE;
    }
    bLastMode = SetKMode(TRUE);
    if( 0 != nand_readbytes(chip, rowAddr, columnAddr, Data, 4))
    {
        SetKMode (bLastMode); 
        return(TRUE);
    }
    SetKMode (bLastMode);
    if(0xff != Data[1])
    {
        return(TRUE);
    }
    
    return(FALSE);
}
//------------------------------------------------------------------------------
/**
* @BRIEF    Returns the status of the specified block.
* @AUTHOR   linjie
* @DATE     2008-1-6
* @PARAM    
* @RETURN
* @RETVAL
* @NOTE
*/
DWORD FMD_GetBlockStatus(BLOCK_ID blockID)
{
    SECTOR_ADDR Sector;
    SectorInfo SI;
    DWORD dwResult = 0;
    
    if(blockID == 0)
        return (BLOCK_STATUS_READONLY | BLOCK_STATUS_RESERVED);

    if (IsBlockBad(blockID))
        return BLOCK_STATUS_BAD;
    
    Sector = blockID * Nand_Info.FalSectorsPerBlock;
    if (!FMD_ReadSector(Sector, NULL, &SI, 1)) 
//-+ ken        return BLOCK_STATUS_UNKNOWN;
  			return BLOCK_STATUS_BAD;
//<<-- ken        
	if (0xff!=SI.bBadBlock)
		return BLOCK_STATUS_BAD;
//-->> ken
    if (!(SI.bOEMReserved & OEM_BLOCK_READONLY))  
        dwResult |= BLOCK_STATUS_READONLY;
    if (!(SI.bOEMReserved & OEM_BLOCK_RESERVED))  
        dwResult |= BLOCK_STATUS_RESERVED;
    return(dwResult);
}

//------------------------------------------------------------------------------
/**
* @BRIEF    Marks the specified block as bad.
* @AUTHOR   linjie
* @DATE     2008-1-6
* @PARAM    
* @RETURN
* @RETVAL
* @NOTE
*/
static BOOL MarkBlockBad(BLOCK_ID blockID)
{
    BYTE Data[] = {0xFF, 0, 0xFF, 0xFF};
    ULONG chip;
    ULONG rowAddr;
    ULONG columnAddr;
    ULONG t;
    ULONG cnt;
    BYTE vbuf[4];
    BOOL bLastMode;
    
    chip = blockID / pChipInfo->BlockPerChip;
    rowAddr = (blockID % pChipInfo->BlockPerChip) * pChipInfo->PagePerBlock;
    //if(NFLASH_SMALL_PAGE == pChipInfo->ChipType)
    //    rowAddr <<= 2;
    if(NFLASH_SMALL_PAGE == pChipInfo->ChipType)
    {
        rowAddr += 1;
        columnAddr = NFC_LOG_PAGE_SIZE;
    }
    else
    {
        columnAddr = NFC_LOG_PAGE_SIZE + NFC_LOG_SPARE_SIZE + NFC_LOG_PAGE_SIZE;
    }
    bLastMode = SetKMode(TRUE);
    nand_readbytes(chip, rowAddr, columnAddr, Data, 4);
    Data[1] = 0;
    t = 0;
    while(t++ < MAX_TRY)
    {
        if(0 == nand_writebytes(chip, rowAddr, columnAddr, Data, 4))
        {
            if(0 == nand_readbytes(chip, rowAddr, columnAddr, vbuf, 4))
            {
                for(cnt = 0; cnt < 4; cnt++)
                {
                    if(vbuf[cnt] != Data[cnt])
                    {
                        break;
                    }
                }
                if(4 == cnt)
                {
                    SetKMode (bLastMode);
                    return TRUE;
                }
            }
        }
        if((t % 100) == 0)
        {
            DEBUGMSG(1, (TEXT("%d. "), t));
        }
    }
    DEBUGMSG(1, (TEXT("MarkBlockBad failed!\r\n")));
    SetKMode (bLastMode);
    return FALSE;   
}
//------------------------------------------------------------------------------
/**
* @BRIEF    Marks the block with the specified block status.
* @AUTHOR   linjie
* @DATE     2008-1-6
* @PARAM    
* @RETURN
* @RETVAL
* @NOTE
*/
BOOL FMD_SetBlockStatus(BLOCK_ID blockID, DWORD dwStatus)
{
    if(blockID == 0)
        return FALSE;

    if (dwStatus & BLOCK_STATUS_BAD)
    {
        if (!MarkBlockBad(blockID))
            return(FALSE);
    }

    if (dwStatus & (BLOCK_STATUS_READONLY | BLOCK_STATUS_RESERVED))
    {
        
        SECTOR_ADDR Sector;
        SectorInfo SI;
        SectorInfo SIr;
        ULONG t;
        ULONG cnt;
//+ ken
		BYTE bufSect[4096];

        Sector = blockID * Nand_Info.FalSectorsPerBlock;
//-+ ken        if (!FMD_ReadSector(Sector, NULL, &SI, 1)) {
        if (!FMD_ReadSector(Sector, bufSect, &SI, 1)) {
            return FALSE;
        }

        if (dwStatus & BLOCK_STATUS_READONLY) {
            SI.bOEMReserved &= ~OEM_BLOCK_READONLY;
        }
        if (dwStatus & BLOCK_STATUS_RESERVED) {
            SI.bOEMReserved &= ~OEM_BLOCK_RESERVED;
        }
        t = 0;
        while(t++ < MAX_TRY)
        {
//-+ ken            if(FMD_WriteSector(Sector, NULL, &SI, 1))
            if(FMD_WriteSector(Sector, bufSect, &SI, 1))
            {
                if(FMD_ReadSector(Sector, NULL, &SIr, 1))
                {
                    for(cnt = 0; cnt < sizeof(SectorInfo); cnt++)
                    {
                        if(*((BYTE *)&SI + cnt) != *((BYTE *)&SIr + cnt))
                        {
                            break;
                        }
                    }
                    if(sizeof(SectorInfo) == cnt)
                    {
                        return TRUE;
                    }
                }
            }
//+ ken
            FMD_EraseBlock(blockID);
            if((t % 100) == 0)
            {
                DEBUGMSG(1, (TEXT("%d. "), t));
            }
        }
        DEBUGMSG(1, (TEXT("FMD_SetBlockStatus failed!\r\n")));
        return FALSE;
    }
    return(TRUE);
}
